/*
 * Copyright 2023 devaper
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.github.devaper.pigeon

import io.github.devaper.pigeon.component.*
import io.github.devaper.pigeon.factory.*
import io.github.devaper.pigeon.intercept.*
import io.github.devaper.pigeon.support.*
import java.util.function.Supplier


class Pigeon(
    private val clientFactory: ClientFactory = CountDownLatchHttpClientFactory()
) : ClientFactory {

    // region basic info

    private var basePath: String? = null

    private var basePathSupplier: Supplier<String>? = null

    fun basePath(value: String): Pigeon {
        this.basePath = value
        return this
    }

    fun basePathSupplier(supplier: Supplier<String>): Pigeon {
        this.basePathSupplier = supplier
        return this
    }

    // endregion


    // region core components

    private var httpClientEngine: HttpClientEngine? = null
    private var httpMessageConverter: HttpMessageConverter? = null
    private var requestInterceptor: RequestInterceptorComposite? = null
    private var requestInterceptorMergeEnabled = true

    fun httpClientEngine(value: HttpClientEngine): Pigeon {
        this.httpClientEngine = value
        return this
    }
    fun httpMessageConverter(value: HttpMessageConverter): Pigeon {
        this.httpMessageConverter = value
        return this
    }
    fun requestInterceptor(vararg interceptors: RequestInterceptor): Pigeon {
        if (this.requestInterceptor == null) {
            this.requestInterceptor = RequestInterceptorComposite(interceptors.toList())
        } else {
            this.requestInterceptor = this.requestInterceptor!!.join(interceptors.toList())
        }
        return this
    }

    fun enableRequestInterceptorMerge(): Pigeon {
        this.requestInterceptorMergeEnabled = true
        return this
    }
    fun disableRequestInterceptorMerge(): Pigeon {
        this.requestInterceptorMergeEnabled = false
        return this
    }

    // endregion


    // region fallback

    private var fallbackInstance: Any? = null

    fun fallback(instance: Any): Pigeon {
        fallbackInstance = instance
        return this
    }

    // endregion


    // region retry

    private var retryEnabled = true
    private var retryAttemptCount = RetryConfig.DEFAULT_ATTEMPT_COUNT

    fun enableRetry(): Pigeon {
        this.retryEnabled = true
        return this
    }

    fun disableRetry(): Pigeon {
        this.retryEnabled = false
        return this
    }

    fun retryAttemptCount(count: Int): Pigeon {
        this.retryAttemptCount = count
        return this
    }

    // endregion


    fun <T> createClient(clientInterface: Class<T>): T {
        val clientConfig = ClientConfig(
            basePathSupplier = if (basePathSupplier != null) basePathSupplier else (Supplier<String> { basePath }),
            httpClientEngine = httpClientEngine,
            httpMessageConverter = httpMessageConverter,
            requestInterceptor = requestInterceptor,
            fallbackInstance = fallbackInstance,
            retry = RetryConfig(
                enabled = retryEnabled,
                attemptCount = retryAttemptCount
            )
        )
        return createClient(clientConfig, clientInterface)
    }

    override fun <T> createClient(clientConfig: ClientConfig, clientInterface: Class<T>): T {
        val config = clientConfig.clone()
        // basic info
        if (config.basePathSupplier == null && this.basePath != null) {
            config.basePathSupplier = Supplier<String> { this.basePath }
        }
        // core components
        if (config.httpClientEngine == null && this.httpClientEngine != null) {
            config.httpClientEngine = this.httpClientEngine
        }
        if (config.httpMessageConverter == null && this.httpMessageConverter != null) {
            config.httpMessageConverter = this.httpMessageConverter
        }
        if (config.requestInterceptor == null) {
            config.requestInterceptor = this.requestInterceptor
        } else if (this.requestInterceptorMergeEnabled && this.requestInterceptor != null) {
            config.requestInterceptor = RequestInterceptorComposite.merge(this.requestInterceptor!!, config.requestInterceptor!!)
        }
        // fallback
        if (config.fallbackInstance == null) {
            config.fallbackInstance = this.fallbackInstance
        }
        // retry
        if (config.retry == null) {
            config.retry = RetryConfig(
                enabled = retryEnabled,
                attemptCount = retryAttemptCount
            )
        }
        return clientFactory.createClient(clientConfig, clientInterface)
    }

}